/*
 * Uart.c
 *
 *  Created on: Oct 13, 2018
 *      Author: ax
 */
#include <Device.h>
#include <Hw/Uart.h>

#include <ti/csl/cslr_uart.h>
#include <ti/csl/cslr_device.h>


#define OSM_FACTOR  16
#define DIVISOR     (CLK_MAIN / (6 * OSM_FACTOR * BAUD_PS))


#define hUart ((CSL_UartRegsOvly) CSL_UART_REGS)


void Uart_init(void) {
    Uint32 reg;

    //make sure TX and RX is reset in case it was enabled from last run.
    reg = hUart->PWREMU_MGMT;
    CSL_FINST(reg, UART_PWREMU_MGMT_UTRST, RESET);
    CSL_FINST(reg, UART_PWREMU_MGMT_URRST, RESET);
    hUart->PWREMU_MGMT = reg;

    CSL_FINST(hUart->MDR, UART_MDR_OSM_SEL, _16XOVERSAMPLING);

    CSL_FINS(hUart->DLL, UART_DLH_DLH, DIVISOR & 0xFF);
    CSL_FINS(hUart->DLH, UART_DLL_DLL, (DIVISOR & 0xFF00) >> 8);

    //FIFO mode is used
    reg = hUart->FCR;
    CSL_FINST(reg, UART_FCR_FIFOEN, ENABLE);
    CSL_FINST(reg, UART_FCR_RXFIFTL, CHAR14);
    CSL_FINST(reg, UART_FCR_DMAMODE1, ENABLE);
    hUart->FCR = reg;

    reg = hUart->LCR;
//    if (pUARTCfg->parityMode == PARITY_DISABLE)
//    {
        CSL_FINST(reg, UART_LCR_PEN, DISABLE);
        CSL_FINST(reg, UART_LCR_EPS, ODD);
        CSL_FINST(reg, UART_LCR_SP,  DISABLE);
//    }
//    else if (pUARTCfg->parityMode == ODD_PARITY_ENABLE_SET1)
//    {
//        CSL_FINS(uart->LCR, UART_LCR_PEN, CSL_UART_LCR_PEN_ENABLE);
//        CSL_FINS(uart->LCR, UART_LCR_EPS, CSL_UART_LCR_EPS_ODD);
//        CSL_FINS(uart->LCR, UART_LCR_SP,  CSL_UART_LCR_SP_DISABLE);
//    }
//    else if (pUARTCfg->parityMode == EVEN_PARITY_ENABLE_SET1)
//    {
//        CSL_FINS(uart->LCR, UART_LCR_PEN, CSL_UART_LCR_PEN_ENABLE);
//        CSL_FINS(uart->LCR, UART_LCR_EPS, CSL_UART_LCR_EPS_EVEN);
//        CSL_FINS(uart->LCR, UART_LCR_SP,  CSL_UART_LCR_SP_DISABLE);
//    }
//    else if (pUARTCfg->parityMode == STICK_PARITY_ENABLE_SET)
//    {
//        CSL_FINS(uart->LCR, UART_LCR_PEN, CSL_UART_LCR_PEN_ENABLE);
//        CSL_FINS(uart->LCR, UART_LCR_EPS, CSL_UART_LCR_EPS_ODD);
//        CSL_FINS(uart->LCR, UART_LCR_SP,  CSL_UART_LCR_SP_ENABLE);
//    }
//    else if (pUARTCfg->parityMode == STICK_PARITY_ENABLE_CLR)
//    {
//        CSL_FINS(uart->LCR, UART_LCR_PEN, CSL_UART_LCR_PEN_ENABLE);
//        CSL_FINS(uart->LCR, UART_LCR_EPS, CSL_UART_LCR_EPS_EVEN);
//        CSL_FINS(uart->LCR, UART_LCR_SP,  CSL_UART_LCR_SP_ENABLE);
//    }

    CSL_FINST(reg, UART_LCR_STB, _1BIT);
    CSL_FINST(reg, UART_LCR_WLS, _8BITS);
    hUart->LCR = reg;

    reg = hUart->MCR;
//    if (pUARTCfg->autoFlow== AUTO_FLOW_DIS)
//    {
        CSL_FINST(reg, UART_MCR_AFE, DISABLE);
        CSL_FINST(reg, UART_MCR_RTS, DISABLE);
//    }
//    else if (pUARTCfg->autoFlow == AUTO_FLOW_CTS_EN)
//    {
//        CSL_FINS(uart->MCR, UART_MCR_AFE, CSL_UART_MCR_AFE_ENABLE);
//        CSL_FINS(uart->MCR, UART_MCR_RTS, CSL_UART_MCR_RTS_DISABLE);
//    }
//    else if (pUARTCfg->autoFlow == AUTO_FLOW_RTS_CTS_EN)
//    {
//        CSL_FINS(uart->MCR, UART_MCR_AFE, CSL_UART_MCR_AFE_ENABLE);
//        CSL_FINS(uart->MCR, UART_MCR_RTS, CSL_UART_MCR_RTS_ENABLE);
//    }

    CSL_FINST(reg, UART_MCR_LOOP, DISABLE);
    hUart->MCR = reg;

    //enable UART
    reg = hUart->PWREMU_MGMT;
    CSL_FINST(reg, UART_PWREMU_MGMT_UTRST, ENABLE);
    CSL_FINST(reg, UART_PWREMU_MGMT_URRST, ENABLE);
    CSL_FINST(reg, UART_PWREMU_MGMT_FREE, STOP);
    hUart->PWREMU_MGMT = reg;

    // receive interrupts
    reg = hUart->IER;
    CSL_FINST(reg, UART_IER_ELSI, ENABLE);
    CSL_FINST(reg, UART_IER_ERBI, ENABLE);
    hUart->IER = reg;

    //uart->IER = 0xFFFFFFFF;
}

Int16           //valid if < uint8_MAX, -1 for no input
Uart_getChar(
    void
){
    if (CSL_FEXT(hUart->LSR, UART_LSR_DR))
        return hUart->RBR;
    else
        return Uart_NO_RX_DATA;
}

void
Uart_putChar(
    const char ch
){
    while (!CSL_FEXT(hUart->LSR, UART_LSR_THRE));
    hUart->THR = ch;
}


#include <stdarg.h>
#include <string.h>
extern _CODE_ACCESS int __TI_printfi(char ** __restrict _format,
                                     va_list _ap, void * __restrict _op,
                                     int (*_outc)(char, void *),
                                     int (*_outs)(char *, void *,int));

/*****************************************************************************/
/* _OUTC -  Put a character in a string                                      */
/*****************************************************************************/
static int _outc(char c, void *_op) {
    static char last = 0;

    if ((c == '\n') && (last != '\r'))
        Uart_putChar('\r');

    Uart_putChar(last = c);

    return *(*((char **)_op))++ = c;
}

/*****************************************************************************/
/* _OUTS -  Append a string to another string                                */
/*****************************************************************************/
static int _outs(char *s, void *_op, int len) {
    int toTx = -1;
    while (++toTx < len)
        Uart_putChar(*(s + toTx));

    memcpy(*((char **)_op), s, len);

    return len;
}

char __buffer(Uart) Uart_printfBuffer[0x2000];

int
Uart_printf(
    const char* fmt,
    ...
){
    va_list _ap;
    int   rval;
    char *fptr = (char *)fmt;
    char *out_end = &Uart_printfBuffer[0];

    va_start(_ap, fmt);
    rval = __TI_printfi(&fptr, _ap, (void *)&out_end, _outc, _outs);
    va_end(_ap);

    *out_end++ = '\0';
    *(int*)out_end++ = rval;

    return rval;
}

